#include <hidef.h> /* for EnableInterrupts macro */
#include "derivative.h" /* include peripheral declarations */
#include <string.h>
#include <stdio.h>
#include "I2C.h"

/*
  Defines
*/
#define PLUG1 PTA_PTA0
#define PLUG1_ON{PTA_PTA0 = 1;}
#define PLUG1_OFF{PTA_PTA0 = 0;}
#define PLUG2 PTA_PTA1
#define PLUG2_ON{PTA_PTA1 = 1;}
#define PLUG2_OFF{PTA_PTA1 = 0;}
#define PLUG3 PTA_PTA4
#define PLUG3_ON{PTA_PTA4 = 1;}
#define PLUG3_OFF{PTA_PTA4 = 0;}
#define PLUG4 PTA_PTA5
#define PLUG4_ON{PTA_PTA5 = 1;}
#define PLUG4_OFF{PTA_PTA5 = 0;}
#define RXSIZE 128
#define ERARNGE 0xFCBE      /* FLASH erase routine in internal ROM */
#define PRGRNGE 0xFC06  /* FLASH program routine in internal ROM */
#define LDRNGE  0xFF30  /* Load FLASH data into RAM data array */

//#############################################################
//                                                            #
//                 Global Constant Variables                  #                
//                                                            #
//#############################################################
#pragma CONST_SEG DEFAULT
const unsigned char WRITE=0;
const unsigned char READ=1; 
const unsigned char RTC_ADDR=0b11010000;
const unsigned char EEPROM_ADDR=0b10100000;
const int PLUG1_ADDR=0xEF00;
const int PLUG2_ADDR=0xEF80;
const int PLUG3_ADDR=0xF000;
const int PLUG4_ADDR=0xF080;
typedef struct {
  char onHour;
  char onMin;
  char onDay;
  char offHour;
  char offMin;
  char offDay;    
} TimeSlotPair;


//#############################################################
//                                                            #
//                      Global Variables                      #                
//                                                            #
//#############################################################
#pragma DATA_SEG __SHORT_SEG MY_ZEROPAGE //256 bytes
unsigned char BUS_SPD @0x0060; /* Value should equal 4x Bus speed */
unsigned char DATASIZE @0x0061; /* Number of bytes in data array (max 255) */
unsigned int  ADDR @0x0062; /* Start address of FLASH to manipulate */
unsigned char flashBuffer[128] @0x0064;
unsigned int *FILE_PTR = (unsigned int *)(&BUS_SPD); /* Address of RAM DATA Block */ 
unsigned char delay0;
unsigned char delay1;
unsigned char currentMinute,currentHour,currentDay;
unsigned char getNewProgrammedTime = 1;
TimeSlotPair timeSlot[4];
unsigned char timerCount;

//#############################################################
//                                                            #
//                   Queue Functions                          #
//                                                            #
//#############################################################
#pragma DATA_SEG __SHORT_SEG MY_ZEROPAGE
unsigned char rxReadIndex,rxWriteIndex,rxCharCount;
#pragma DATA_SEG DEFAULT
unsigned char rxBuffer[RXSIZE];


#pragma CODE_SEG DEFAULT
/*******************************
 * queue_init - initialize queue
 *******************************/
void queue_init(void){
  rxReadIndex=rxWriteIndex=rxCharCount=0;      
}

/********************************************************
 * queue_size - returns number of char in queue
 * Main routine checks if char is in queue before reading
 ********************************************************/
unsigned char rxBuffer_size(void){
 return rxCharCount;   
}

//#############################################################
//                                                            #
//                     SCI Functions                          #
//                                                            #
//#############################################################

/************************************
 * SCI_init - Initialize the SCI port
 ************************************/
void SCI_init(void) {    
    SCC1 = 0b01000100;       //Enable SCI,8N1,    
    //9.6k (1,1)
    SCBR_SCP = 1;            // Baud rate prescaler = 0
    SCBR_SCR = 1;            // Baud rate divisor = 0    
    SCC2_RE = 1;             // Enable receiver
    SCC2_SCRIE = 1;          // Enable recieve interrupt
    SCC2_TE = 1;             // Enable transmitter        
    
}

/************************************
 * sciread - Read a byte from SCI port
 ************************************/
unsigned char sciread() 
{
  unsigned char byte;
  if(rxCharCount){
   byte = rxBuffer[rxReadIndex++];
   rxReadIndex &= RXSIZE-1;
   rxCharCount--;   
   return byte;
    
  } else
  return 0;
}

/***********************************
 * sciout - Output a byte to SCI port
 ***********************************/

void sciout(unsigned char ch)
{
    while (SCS1_SCTE == 0);
    SCDR = ch;
}

/************************************************************
 * sciouts - Print the contents of the character string "s" 
 * to the SCI port. String must be terminated by a zero byte.
 ************************************************************/
void sciouts(unsigned char *s)
{
    unsigned char ch;
    while ((ch = *s++) != (unsigned char) NULL)
        sciout(ch);
}

/****************************
 * SCI Recieve Interrupt
 * Add recieved data to queue
 ****************************/
interrupt 13 void scirISR(void){           
    SCS1 &= ~(0x20);                
    rxBuffer[rxWriteIndex++]=SCDR;
    rxWriteIndex &= RXSIZE-1;
    rxCharCount++;             
}

//#############################################################
//                                                            #
//                     RTC Functions                          #
//                                                            # 
//#############################################################

/****************************************************
 * rtc_init - initializes the RTC Tuesday 4/4/09
 ****************************************************/
void rtc_init(void){
  I2C_start();
  I2C_tx_byte(RTC_ADDR|WRITE); //RTC Address
  I2C_tx_byte(0); //Memory address 0
  I2C_tx_byte(0); //Start Oscillator, 0 seconds
  I2C_tx_byte(0);	//0 minutes
  I2C_tx_byte(0); //24 hour mode, 0 hours
  I2C_tx_byte(4); //Day 4, Thursday
  I2C_tx_byte(2); //Date 2nd
  I2C_tx_byte(4); //April
  I2C_tx_byte(9); //2009
  I2C_tx_byte(0); //Control register to 0
  I2C_stop();     
}

/****************************************************
 * RTC_Update - gets the Time from the RTC
 * Gets second, minute, hour, day, date, month, year
 ****************************************************/
void RTC_Update(){
  I2C_start();
  I2C_tx_byte(RTC_ADDR|WRITE);
  I2C_tx_byte(0);
  I2C_start();
  I2C_tx_byte(RTC_ADDR|READ);
  I2C_rx_byte(1);
  currentMinute=I2C_rx_byte(1);
  currentHour=I2C_rx_byte(1);
  currentDay=I2C_rx_byte(0);
  I2C_stop();    
}

//#############################################################
//                                                            #
//                  Debug Menu Functions                      #
//                                                            #
//#############################################################
/****************************************************
 * printTime - Prints time to terminal
 * Time: 23:59:59 Date: 12/31/99
 ****************************************************/

void printTime(){ 
  sciout('|'); 
  sciout(((currentHour&0x30)>>4)+48);
  sciout((currentHour&0x0F)+48);  
  sciout(':');
  sciout(((currentMinute&0x70)>>4)+48);
  sciout((currentMinute&0x0F)+48); 
  sciout('|');        
  sciout((currentDay&0x07)+48);      
  sciout(13);
  sciout(10);   
}

//#############################################################
//                                                            #
//                    Timer Functions                         #
//                                                            #
//#############################################################

 /****************************************************
 * Timer_init - Setup timer interrupt
 ****************************************************/
void Timer_init(){
    /*
       The board has a 14.7456 MHz clock which makes the processor run
       at 3.6864 MHz.  We want the timer to interrupt every second
       (1 Hz) so we need to count clocks to 3.6864MHz/1Hz = 3,686,400.  This
       is too big for the 16 bit counter register so use the prescaler to
       divide the clock by 64 and then count that clock to 57,600. 1 Second 
    */   
    T1SC_PS = 6;             // set prescalar for divide by 64
    T1SC_TOIE = 1;           // enable timer interrupt
    T1MOD = 57600;           // store modulo value in T1MODH:T1MODL
    T1SC_TSTOP = 0;          // start timer running  
}


/****************************************************
 * timerISR - Every 1 second, check to turn off plugs
 ****************************************************/
interrupt 6 void timerISR(void){       
  //Compare Times
  T1SC_TOF = 0;
  sciout('t');
  timerCount++;
  if(timerCount == 60){
    timerCount = 0;
    //checkPlugs();  
  }
       
}

//#############################################################
//                                                            #
//                    Helper Functions                        #
//                                                            #
//#############################################################

/***************************************************
 * del1m - Delay about 1msec times the "d" argument.
 ***************************************************/
void del1m(int d)
{
    while (d--) {
        asm {
            ; The following code delays 1 millisecond by looping.
            ; Total time is 4 + 7 * (4 + 77 * (4 + 3) + 4 + 3) = 3854 cycles
            ; A 14.7456MHz external clock gives an internal CPU clock of
            ; 3.6864MHz (271.267ns/cycle).  Delay is then 1.04546 milliseconds.
                    mov     #$7,delay1  ; 4 cycles
            m1:     mov     #77,delay0  ; 4 cycles
            m0:     dec     delay0      ; 4 cycles
                    bne     m0          ; 3 cycles
                    dec     delay1      ; 4 cycles
                    bne     m1          ; 3 cycles
        }
    }
}



/****************************************************
 * Plug_init - initializes I/O pins for Plugs
 ****************************************************/
void Plug_init(){
  DDRA_DDRA0 = 1;   /* Set I/O Pin as Output */
  DDRA_DDRA1 = 1;   /* Set I/O Pin as Output */
  DDRA_DDRA4 = 1;   /* Set I/O Pin as Output */
  DDRA_DDRA5 = 1;   /* Set I/O Pin as Output */
  PLUG1_ON;         /* Turn Plug 1 on */
  PLUG2_ON;         /* Turn Plug 2 on */
  PLUG3_ON;         /* Turn Plug 3 on */
  PLUG4_ON;         /* Turn Plug 4 on */  
}

//#############################################################
//                                                            #
//                     FLASH Functions                        #
//                                                            #
//#############################################################
void FLASHErase(int address) {
  ADDR = address;
  asm(LDHX FILE_PTR);    /* Load address of RAM Data Block to H:X */
  asm(JSR ERARNGE);      /* Call ERARNGE ROM Subroutine */ 
  ADDR = address+0x40;
  asm(LDHX FILE_PTR);    /* Load address of RAM Data Block to H:X */
  asm(JSR ERARNGE);      /* Call ERARNGE ROM Subroutine */ 
  EnableInterrupts;   
}

void FLASHProgram(int address) {
  FLASHErase(address);
  ADDR = address;
  asm(LDHX FILE_PTR);    /* Load address of RAM Data Block to H:X */
  asm(JSR PRGRNGE);      /* Call PRGRNGE ROM Subroutine */ 
  EnableInterrupts;   
}
  
void FLASHLoad(int address) {
  ADDR = address;
  asm(LDHX FILE_PTR);    /* Load address of RAM Data Block to H:X */
  asm(JSR LDRNGE);    /* Call LDRNGE ROM Subroutine */
  EnableInterrupts;  
}

void clearArray(){
  int i;
  for(i=0; i<128;i++){    
    flashBuffer[i]=0; 
  }
}

//#############################################################
//                                                            #
//                  Programming Functions                     #
//                                                            #
//#############################################################
void setTimeSlot(){
   
}

void getTimeSlot(){
  
}

void setTime(){
  while(rxBuffer_size()==0)
    currentHour = sciread();
  while(rxBuffer_size()==0)
    currentMinute = sciread();
  while(rxBuffer_size()==0)
    currentDay = sciread();  
}

void getTime(){
  sciout(currentHour);
  sciout(currentMinute);
  sciout(currentDay);  
}

//#############################################################
//                                                            #
//                      Main Routine                          #
//                                                            #
//#############################################################
void main(void) {

  //Variables  
  unsigned char in,minute,second,hour,day,date,month,year;  

  //Initialize
  CONFIG1_COPD = 1; /* disable COP reset */
  EnableInterrupts; /* enable interrupts */  
  queue_init();     /* initialize queue */
  SCI_init();       /* initialize SCI */  
  I2C_init();       /* initialize I2C */ 
  Plug_init();      /* initialize Plug I/O pins */ 
  Timer_init();     /* initialize Timer interrupt */
  DATASIZE = 128;   /* flash data block of 128 bytes */
  BUS_SPD = 15;     /* 3.6864*4 */ 
  timerCount = 0;
  
  //Main loop
  for(;;) {  
  /*
    if(getNewProgrammedTime){ 
      getNewProgrammedTime = 0; //Set in timer function that updates RTC and check if we're out of range
      //Go into flash and get new programmed time.
    }
    */
    
    //Check for char in buffer     
    if(rxBuffer_size()!=0){

      //Read buffer  
      in=sciread();    
      //Do Action         
      switch(in){
        case '1': //Get Time
                RTC_Update();                
                printTime();               
                break;
        case '2': //Reset RTC
                rtc_init();                  
                break;
        case 't':
                for(;;){
                 RTC_Update(); 
                 printTime();
                }
        case 'a':
              if(PLUG1){    
                PLUG1_OFF;
              } else {
                PLUG1_ON; 
              }
              break;
        case 'b':
              if(PLUG2){    
                PLUG2_OFF;
              } else {
                PLUG2_ON; 
              }
              break;
        case 'c':
              if(PLUG3){    
                PLUG3_OFF;
              } else {
                PLUG3_ON; 
              }
              break;
        case 'd':
              if(PLUG4){    
                PLUG4_OFF;
              } else {
                PLUG4_ON; 
              }     
              break;    
        case 'e':
              sciout(10);
              sciout(13);
              clearArray();
              FLASHErase(PLUG1_ADDR);
              flashBuffer[0] = 'a';
              FLASHProgram(PLUG1_ADDR);
              sciout(flashBuffer[0]);              
              clearArray();
              sciout(flashBuffer[0]);
              FLASHLoad(PLUG1_ADDR);
              sciout(flashBuffer[0]);
              sciout(10);
              sciout(13);  
                          
              break;
        case 0xFC: //Set Time Slots
              setTimeSlot();
              break;              
        case 0xFD: //Get Time Slots
              getTimeSlot();
              break;
        case 0xFE: //Set Time
              setTime();
              break;
        case 0xFF: //Get Time
              getTime();
              break;
        default: //Else
                break;
      }      
    
    }                       
  
  } /* loop forever */

}

